﻿namespace Commands.Framework
{
    using System;
    using System.Windows.Input;

    /// <summary>
    /// An implementation of <see cref="ICommand"/> that allows the client to provide delegates for execution.
    /// <remarks>This class enables data binding to methods.</remarks>
    /// </summary>
    public class DelegatingCommand : ICommand
    {
        private readonly Action _callback;
        private readonly Func<object, bool> _canExectue;
        private readonly Action<object> _execute;
        private readonly bool _isAsync;

        /// <summary>
        /// Initializes a new instance of the <see cref="DelegatingCommand"/> class.
        /// </summary>
        /// <param name="execute">The execute method.</param>
        /// <param name="canExectue">The can exectue method.</param>
        /// <param name="isAsync">Indicates whether to execute the command asynchronously or not.</param>
        /// <param name="callback">The callback method for async execution.</param>
        public DelegatingCommand(Action<object> execute, Func<object, bool> canExectue, bool isAsync, Action callback)
        {
            _execute = execute;
            _canExectue = canExectue;
            _isAsync = isAsync;
            _callback = callback;
        }

        #region ICommand Members

        /// <summary>
        /// Occurs when changes occur that affect whether or not the command should execute.
        /// </summary>
        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        /// <summary>
        /// Defines the method to be called when the command is invoked.
        /// </summary>
        /// <param name="parameter">Data used by the command.  If the command does not require data to be passed, this object can be set to null.</param>
        public void Execute(object parameter)
        {
            if (_canExectue(parameter))
            {
                if (_isAsync)
                {
                    if (_callback == null)
                        Framework.Execute.OnBackgroundThread(() => _execute(parameter));
                    else Framework.Execute.OnBackgroundThread(() => _execute(parameter), _callback);
                }
                else _execute(parameter);
            }
        }

        /// <summary>
        /// Defines the method that determines whether the command can execute in its current state.
        /// </summary>
        /// <param name="parameter">Data used by the command.  If the command does not require data to be passed, this object can be set to null.</param>
        /// <returns>
        /// true if this command can be executed; otherwise, false.
        /// </returns>
        public bool CanExecute(object parameter)
        {
            return _canExectue(parameter);
        }

        #endregion
    }
}